#ifndef permutator_HPP__
#define permutator_HPP__

#include <climits> //CHAR_BIT

#include <boost/assert.hpp>
#include <boost/static_assert.hpp>
#include <boost/mpl/at.hpp>
#include <boost/mpl/size.hpp>

#include "bit_manip.hpp"


namespace bit_adjuster
{
	//Least Significant Bit is on the right - Default BIT Endianess
	template <typename IndexType, IndexType Index, IndexType CharLenght = CHAR_BIT>
	struct little_endian
	{
		static const IndexType value = Index;
	};

	//Least Significant Bit is on the right
	template <typename IndexType, IndexType Index, IndexType CharLenght = CHAR_BIT>
	struct little_endian_des_tables
	{
		static const IndexType new_index = Index-1;
		static const IndexType value = new_index;
	};

	//Least Significant Bit is on the left
	template <typename IndexType, IndexType Index, IndexType CharLenght = CHAR_BIT>
	struct big_endian
	{
		static const IndexType value = (((( Index / CharLenght )+1)*CharLenght)-1) - (Index % CharLenght);
	};

	//Least Significant Bit is on the left
	template <typename IndexType, IndexType Index, IndexType CharLenght = CHAR_BIT>
	struct big_endian_des_tables
	{
		static const IndexType new_index = Index-1;
		static const IndexType value = (((( new_index / CharLenght )+1)*CharLenght)-1) - (new_index % CharLenght);
	};
}


template <
	typename PermutationTable
	, typename SourceArrayType
	, size_t SourceSize
	, typename TargetArrayType
	, size_t TargetSize
	, template <typename IndexType, IndexType Index, IndexType CharLenght> class SourceIndexTraits
	, template <typename IndexType, IndexType Index, IndexType CharLenght> class TargetIndexTraits
	, typename PermutationTable::value_type SourceCharLenght = CHAR_BIT
	, typename PermutationTable::value_type TargetCharLenght = CHAR_BIT
	>
struct permutator_array_impl
{
	typedef typename PermutationTable::value_type index_type;

	template < index_type Index >
	inline static void call( const SourceArrayType (&source)[SourceSize], TargetArrayType (&target)[TargetSize] )
	{
		//TODO: verificar si index_type boost::is_integral
		call_impl<Index>( source, target );
		call<Index-1>( source, target );
	}

	template <>
	inline static void call<0>( const SourceArrayType (&source)[SourceSize], TargetArrayType (&target)[TargetSize] )
	{
		call_impl<0>( source, target );
	}

private:

	template < index_type Index >
	static void call_impl( const SourceArrayType (&source)[SourceSize], TargetArrayType (&target)[TargetSize] )
	{
		static const index_type tmp_idx = Index;
		static const index_type perm_idx = boost::mpl::at_c<PermutationTable, Index>::type::value;
		
		//const index_type source_idx = SourceIndexTraits<index_type, perm_idx-1, SourceCharLenght>::value;
		const index_type source_idx = SourceIndexTraits<index_type, perm_idx, SourceCharLenght>::value;


		const int perm_value = get_bit_value<source_idx>( source );		//TODO: int?

		if ( perm_value != 0 )
		{
			const index_type target_idx = TargetIndexTraits<index_type, Index, TargetCharLenght>::value;
			set_bit_value<target_idx>(target);
		}
	}
};

template <
	typename PermutationTable
	, typename CharType
	, template <typename IndexType, IndexType Index, IndexType CharLenght> class SourceIndexTraits
	, template <typename IndexType, IndexType Index, IndexType CharLenght> class TargetIndexTraits
	, typename PermutationTable::value_type SourceCharLenght = CHAR_BIT
	, typename PermutationTable::value_type TargetCharLenght = CHAR_BIT
>
struct permutator_char_impl
{
	typedef typename PermutationTable::value_type index_type;

	template < index_type Index >
	inline static void call( CharType source, CharType & target )
	{
		//TODO: verificar si index_type boost::is_integral
		call_impl<Index>( source, target );
		call<Index-1>( source, target );
	}

	template <>
	inline static void call<0>( CharType source, CharType & target )
	{
		call_impl<0>( source, target );
	}

private:

	template < index_type Index >
	static void call_impl( CharType source, CharType & target )
	{
		//const index_type tmp_idx = Index;
		//const index_type source_chr_lenght = SourceCharLenght;
		//const index_type target_chr_lenght = TargetCharLenght;

		static const index_type perm_idx   = boost::mpl::at_c<PermutationTable, Index>::type::value;
		static const index_type source_idx = SourceIndexTraits<index_type, perm_idx, SourceCharLenght>::value;
		const int perm_value = get_bit_value<source_idx>( source );		//TODO: int?

		if ( perm_value != 0 )
		{
			static const index_type target_idx = TargetIndexTraits<index_type, Index, TargetCharLenght>::value;
			set_bit_value<target_idx>(target);
		}
	}
};

template < 
	typename PermutationTable
	, template <typename IndexType, IndexType Index, IndexType CharLenght> class SourceIndexTraits// = bit_adjuster::big_endian
	, template <typename IndexType, IndexType Index, IndexType CharLenght> class TargetIndexTraits// = bit_adjuster::big_endian
	, typename PermutationTable::value_type SourceCharLenght = CHAR_BIT
	, typename PermutationTable::value_type TargetCharLenght = CHAR_BIT
>
struct permutator
{
	template < typename SourceArrayType, size_t SourceSize, typename TargetArrayType, size_t TargetSize	>
	inline static void call( const SourceArrayType (&source)[SourceSize], TargetArrayType (&target)[TargetSize] )
	{
		typedef permutator_array_impl<
			PermutationTable
			, SourceArrayType
			, SourceSize
			, TargetArrayType
			, TargetSize
			, SourceIndexTraits
			, TargetIndexTraits
			, SourceCharLenght
			, TargetCharLenght
		> permutator;

		permutator::call<boost::mpl::size<PermutationTable>::value-1>( source, target );
	}

	template < typename CharType >
	inline static void call( CharType source, CharType & target )
	{
		typedef permutator_char_impl<
			PermutationTable
			, CharType
			, SourceIndexTraits
			, TargetIndexTraits
			, SourceCharLenght
			, TargetCharLenght
		> permutator;

		permutator::call<boost::mpl::size<PermutationTable>::value-1>( source, target );
	}
};




#endif // permutator_HPP__